package ferp.core.mode;

import ferp.core.card.Card;
import ferp.core.game.Game;
import ferp.core.game.Input;
import ferp.core.game.Settings;
import ferp.core.log.Log;
import ferp.core.player.Profile;
import ferp.core.state.State;

import java.util.HashMap;

/**
 * User: olegs
 * Date: 19/05/11 14:51
 */
public abstract class Mode
{

  public static Mode get(String name) {return modes.get(name);}

  public static boolean isMoving(Mode mode) {return mode == misere || mode == passing || mode == tricking || mode == tricking10;}

  public Mode() {modes.put(getClass().getName(), this);}

  // mode initialization
  public int initialize(Game.Listener listener, Profile profile, Game g, Settings settings) throws Game.Error
  {
    // reset rounds counter
    g.rounds = 0;
    // reset hand
    g.hand = 0;
    // setup the starting player for this round
    g.player.starting = getStartingPlayer(g);

    return State.Result.SCHEDULE;
  }

  public final int fill(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error
  {
    // check for the round solve
    if (g.hand == 0)
    {
      // this should fire specific event (listener.onBiddingRoundStart())
      prepare(listener, g, settings);
      // set the current player to be the first player in round
      g.player.current = getFirstPlayer(g);
    }

    Log.debug(Log.TAG, g.player(g.player.current) + ": getting input options");

    return options(listener, g, settings, input);
  }

  // processing
  public final int process(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error
  {
    // call the actual processing
    switch (act(listener, g, settings, input))
    {
      case NEXT:
        // switch to the next player or exit this round (going to "summarizing" state)
        if (isRoundOver(g))
          exit(g);
        else
          next(g);
        break;

      case STAY:
        break;

      case DONE:
        done(listener, g, settings);
        break;
    }

    return State.Result.SCHEDULE;
  }

  // chill down after round is over but before summarizing
  public int chill(Game.Listener listener, Game g, Settings settings) {return State.Result.SCHEDULE;}

  // we got here when the round is over
  public final int summarize(Game.Listener listener, Game g, Settings settings)
  {
    // analyze round results
    analyze(listener, g, settings);

    // increment rounds counter
    ++g.rounds;
    // reset the hand counter
    g.hand = 0;

    // check if the set is over as well
    if (isSetOver(g))
      done(listener, g, settings);

    return g.options.isHumanInputRequired() ? State.Result.SHOW_OPTIONS : State.Result.SCHEDULE;
  }

  public final void exit(Game g) {g.set(State.chill);}

  public void score(Game g, Settings settings) {Log.debug(Log.TAG, "Score method called for wrong mode " + getClass().getName());}

  public Card.State getTalonCardState(Game g, Settings settings, int i) {return Card.State.FACE_DOWN;}

  protected static enum Act {NEXT, STAY, DONE}

  // get starting player - by default it's the player next to the dealer
  protected int getStartingPlayer(Game g) {return Game.next(g.player.dealer);}
  // called before new round starts
  protected void prepare(Game.Listener listener, Game g, Settings settings) {/* do nothing by default */}
  // get first player - by default it's the starting player
  protected int getFirstPlayer(Game g) {return g.player.starting;}
  // set input options
  protected abstract int options(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error;
  // perform the actual work depending on the specific mode
  protected Act act(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error {return Act.NEXT;}
  // analyze round result
  protected void analyze(Game.Listener listener, Game g, Settings settings) {/* do nothing by default */}
  // return true if round is over
  protected boolean isRoundOver(Game g) {return g.hand >= 2;}
  // return true if set is over (true by default)
  protected boolean isSetOver(Game g) {return true;}
  // cleanup
  protected void cleanup() {/* do nothing by default */}
  // called after set ends
  protected void done(Game.Listener listener, Game g, Settings settings) {/* do nothing by default */}
  // move to the next player
  protected void next(Game g)
  {
    // increment hand
    g.hand = ++g.hand % (Game.PLAYERS + 1);
    // next player
    g.player.current = Game.next(g.player.current);
  }

  // modes hash map
  private static HashMap<String, Mode> modes = new HashMap<String, Mode>();

  // modes
  public static final Dealing dealing = new Dealing();
  public static final Bidding bidding = new Bidding();
  public static final Dropping dropping = new Dropping();
  public static final Contracting contracting = new Contracting();
  public static final Misere misere = new Misere();
  public static final Passing passing = new Passing();
  public static final Tricking tricking = new Tricking();
  public static final Tricking10 tricking10 = new Tricking10();
}
